package ru.fadeev.crammer;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import java.util.Enumeration;
import java.util.Hashtable;


/**
 * Settings class used to keep setting of Crammer program. All members of class
 * is static, because it's only one Settings object for Crammer program, 
 * and this way every object in program can easy access settings.
 * @author Konstantin Fadeyev
 *
 */
public class Settings {
	public final static int BATCH_NAME_MAX_LENGTH = 50;
	public final static int DICTIONARY_NAME_MAX_LENGTH = 100;
	public final static int SHEET_NAME_MAX_LENGTH = 100;
	public final static int WORD_MAX_LENGTH = 50;
	public final static int TRANSCRIPTION_MAX_LENGTH = 50;	
	public final static int TRANSLATION_MAX_LENGTH = 50;	
	public final static int PROMPT_MAX_LENGTH = 200;
	public final static int PHRASE_MAX_LENGTH = 200;
	public final static int SETTINGS_KEY_MAX_LENGTH = 50;
	public final static int SETTINGS_VALUE_MAX_LENGTH = 200;
	public final static int PHRASE_ADDING_MAX_LENGTH = 160;
	public final static long PHRASES_FILE_MAX_SIZE = 100000000;
	public final static int PHRASES_BANK_REQUEST_TIMEOUT = 30000;
	
	
	
	private static Object[][] settingsList = {
		{"minimizeOnClose", new Boolean(false)},
		{"minimizeToTray", new Boolean(true)},
		
		{"testNeedsCheckPeriod", new Long(1200000)},  //20 min
		{"showNeededToTestWindow", new Boolean(true)},
		{"neededToTestWindowNumber", new Integer(30)},		  

		{"translationNumberOfLines", new Integer(25)},
		{"reverseNumberOfLines", new Integer(25)},
		{"transcriptionNumberOfLines", new Integer(25)},
		{"gapsFillingNumberOfLines", new Integer(15)},
		
		{"translationBasePeriod", new Long(240*60000)},
		{"translationFactor", new Double(1.5)},
		{"reverseBasePeriod", new Long(240*60000)},
		{"reverseFactor", new Double(1.5)},
		{"transcriptionBasePeriod", new Long(240*60000)},
		{"transcriptionFactor", new Double(1.5)},
		{"gapsFillingBasePeriod", new Long(480*60000)},
		{"gapsFillingFactor", new Double(1.8)},
		
		{"loadPhrasesFromBankForNewWords", new Boolean(true)},
		{"phrasesBankServer", "www.seomancer.net"},
		{"phrasesBankPort", "80"},
		{"phrasesBankUseProxy", new Boolean(false)},
		{"phrasesBankProxyServer", ""},
		{"phrasesBankProxyPort", ""},
		
		{"addPhrasePoint", new Boolean(true)},
		{"addPhraseExclamation", new Boolean(true)},
		{"addPhraseQuestion", new Boolean(true)},
		{"addPhraseLF", new Boolean(false)},
		{"addPhraseCustomDelimiter", ""},
		{"addPhraseMinLength", new Integer(5)},
		{"addPhrasePreprocessing", new Boolean(true)},
	
		{"lastImportedExported", ""},
		{"lastAddedPhrasesDirectory", ""},
		{"crammerMainFrameX", new Integer(-1)},
		{"crammerMainFrameY", new Integer(-1)},
		{"crammerMainFrameW", new Integer(-1)},
		{"crammerMainFrameH", new Integer(-1)},
		{"crammerMainFrameMaximized", new Boolean(true)},
		{"addEditSheetDialogX", new Integer(-1)},
		{"addEditSheetDialogY", new Integer(-1)},
		{"addEditSheetDialogW", new Integer(-1)},
		{"addEditSheetDialogH", new Integer(-1)},
		{"addEditPromptWindowX", new Integer(-1)},
		{"addEditPromptWindowY", new Integer(-1)},
		{"settingsDialogX", new Integer(-1)},
		{"settingsDialogY", new Integer(-1)},
		{"settingsDialogW", new Integer(-1)},
		{"settingsDialogH", new Integer(-1)},
		{"testTranslationDialogX", new Integer(-1)},
		{"testTranslationDialogY", new Integer(-1)},
		{"testTranslationDialogW", new Integer(-1)},
		{"testTranslationDialogH", new Integer(-1)},
		{"testGapsFillingDialogX", new Integer(-1)},
		{"testGapsFillingDialogY", new Integer(-1)},
		{"testGapsFillingDialogW", new Integer(-1)},
		{"testTranscriptionDialogX", new Integer(-1)},
		{"testTranscriptionDialogY", new Integer(-1)},
		{"testTranscriptionDialogW", new Integer(-1)},
		{"testTranscriptionDialogH", new Integer(-1)},
		{"testPromptWindowX", new Integer(-1)},
		{"testPromptWindowY", new Integer(-1)},
		{"showPromptWindow", new Boolean(false)},
		{"lastTestType", new Integer(TestManager.TRANSLATION)}
	};
	
	private static Hashtable<Object,Object> hashtable = 
		new Hashtable<Object,Object>();
	

	/**
	 * Fills hash table with default values of settings. 
	 * Doesn't save it to database. 
	 */
	private synchronized static void resetToDefaults() {
		hashtable.clear();
		for(int i = 0; i < settingsList.length; i++) {
			hashtable.put(settingsList[i][0], settingsList[i][1]);
		}
	}
	
	/**
	 * Returns boolean value for given setting name
	 * @param key name of required setting
	 * @return value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public synchronized static boolean getBoolean(String key) 
			throws KeyNotFoundException  {
		Boolean value = (Boolean)hashtable.get(key);
		if(value == null) {
			throw new KeyNotFoundException(key);
		}
		return value.booleanValue();
	}
	
	
	/**
	 * Returns integer value for given setting name
	 * @param key name of required setting
	 * @return value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public synchronized static int getInt(String key) 
			throws KeyNotFoundException  {
		Integer value = (Integer)hashtable.get(key);
		if(value == null) {
			throw new KeyNotFoundException(key);
		}
		return value.intValue();
	}
	
	
	/**
	 * Returns double value for given setting name
	 * @param key name of required setting
	 * @return value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public synchronized static double getDouble(String key) 
			throws KeyNotFoundException  {
		Double value = (Double)hashtable.get(key);
		if(value == null) {
			throw new KeyNotFoundException(key);
		}
		return value.doubleValue();
	}
	
	
	/**
	 * Returns long value for given setting name
	 * @param key name of required setting
	 * @return value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public synchronized static long getLong(String key) 
			throws KeyNotFoundException  {
		Long value = (Long)hashtable.get(key);
		if(value == null) {
			throw new KeyNotFoundException(key);
		}
		return value.longValue();
	}
	
	
	/**
	 * Returns string value for given setting name
	 * @param key name of required setting
	 * @return value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public synchronized static String getString(String key) 
			throws KeyNotFoundException  {
		Object value = hashtable.get(key);
		if(value == null) {
			throw new KeyNotFoundException(key);
		}
		return value.toString();
	}
	
	
	/**
	 * Returns default boolean value for given setting name
	 * @param key name of required setting
	 * @return default value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public static boolean getDefaultBoolean(String key) 
			throws KeyNotFoundException {
		for(int i = 0; i < settingsList.length; i++) {
			if(key.equals(settingsList[i][0])) {
				Boolean value = (Boolean)settingsList[i][1];				
				return value.booleanValue();
			}
		}
		throw new KeyNotFoundException(key);
	}
	
	
	/**
	 * Returns default integer value for given setting name
	 * @param key name of required setting
	 * @return default value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public static int getDefaultInt(String key) throws KeyNotFoundException {
		for(int i = 0; i < settingsList.length; i++) {
			if(key.equals(settingsList[i][0])) {
				Integer value = (Integer)settingsList[i][1];				
				return value.intValue();
			}
		}
		throw new KeyNotFoundException(key);
	}
	
	
	/**
	 * Returns default double value for given setting name
	 * @param key name of required setting
	 * @return default value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public static double getDefaultDouble(String key) throws KeyNotFoundException {
		for(int i = 0; i < settingsList.length; i++) {
			if(key.equals(settingsList[i][0])) {
				Double value = (Double)settingsList[i][1];				
				return value.doubleValue();
			}
		}
		throw new KeyNotFoundException(key);
	}
	
	
	/**
	 * Returns default long value for given setting name
	 * @param key name of required setting
	 * @return default value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public static long getDefaultLong(String key) throws KeyNotFoundException {
		for(int i = 0; i < settingsList.length; i++) {
			if(key.equals(settingsList[i][0])) {
				Long value = (Long)settingsList[i][1];				
				return value.longValue();
			}
		}
		throw new KeyNotFoundException(key);
	}
	
	
	/**
	 * Returns default string value for given setting name
	 * @param key name of required setting
	 * @return default value of setting
	 * @throws KeyNotFoundException if given name isn't in settings list
	 */
	public static String getDefaultString(String key) throws KeyNotFoundException {
		for(int i = 0; i < settingsList.length; i++) {
			if(key.equals(settingsList[i][0])) {				
				return settingsList[i][1].toString();
			}
		}
		throw new KeyNotFoundException(key);
	}

	
	/**
	 * Sets new boolean value for setting specified by name 
	 * and save it to database.
	 * @param key name of setting
	 * @param value new value of setting
	 */
	public synchronized static void setBoolean(String key, boolean value) {
		hashtable.put(key, new Boolean(value));
		save(key);
	}
	
	
	/**
	 * Sets new integer value for setting specified by name 
	 * and save it to database.
	 * @param key name of setting
	 * @param value new value of setting
	 */
	public synchronized static void setInt(String key, int value) {
		hashtable.put(key, new Integer(value));
		save(key);
	}
	
	
	/**
	 * Sets new double value for setting specified by name 
	 * and save it to database.
	 * @param key name of setting
	 * @param value new value of setting
	 */
	public synchronized static void setDouble(String key, double value) {
		hashtable.put(key, new Double(value));
		save(key);
	}
	
	
	/**
	 * Sets new long value for setting specified by name 
	 * and save it to database.
	 * @param key name of setting
	 * @param value new value of setting
	 */
	public synchronized static void setLong(String key, long value) {
		hashtable.put(key, new Long(value));
		save(key);
	}

	
	/**
	 * Sets new string value for setting specified by name 
	 * and save it to database.
	 * @param key name of setting
	 * @param value new value of setting
	 */
	public synchronized static void setString(String key, String value) {
		hashtable.put(key, value);
		save(key);
	}
	
	
	/**
	 * Saves default values of settings to database
	 */
	public static void saveDefaults() {	
		resetToDefaults();
    	try {
			Connection connection = DatabaseManager.getConnection();  
			
	    	PreparedStatement statement = connection.prepareStatement(
	    			"INSERT INTO SETTINGS " +
	    			"(NAME, VALUE) values " +
	    			"(?,?)");
	    	Enumeration<Object> k = hashtable.keys();
	    	while(k.hasMoreElements()) {
	    		String key = (String)k.nextElement();
				statement.setString(1, key);
				statement.setString(2, hashtable.get(key).toString());
				statement.addBatch();
			}			
			statement.executeBatch();
	    	statement.close();
	    	connection.close();	    		    	
    	} catch (Exception e) {
	        e.printStackTrace();
	    }	
	}

	
	/**
	 * Saves setting with given name. Used by inner functions that changes
	 * settings.
	 * @param name name of setting to save
	 */
	private static void save(String name) {		
    	try {			
			String value = hashtable.get(name).toString();
			
			Connection connection = DatabaseManager.getConnection();      		
	    	PreparedStatement statement = connection.prepareStatement(
	    			"UPDATE SETTINGS " +
	    			"SET VALUE = ? " + 
	    			"WHERE NAME = ? ");
			statement.setString(1, value);
			statement.setString(2, name);
			statement.execute();
	    	statement.close();
	    	connection.close();	    		    	
    	} catch (Exception e) {
	        e.printStackTrace();
	    }	
	}
	
	
	/**
	 * Loads all settings from database.
	 */
	public static void load() {
		resetToDefaults();
		try {
			Connection connection = DatabaseManager.getConnection();      
			Statement statement = connection.createStatement();;
			ResultSet resultSet = statement.executeQuery(
        			"SELECT name, value " +
        			"FROM SETTINGS");	
        	while(resultSet.next())
        	{
        	    String name = resultSet.getString(1);
        	    String valueClass = "java.lang.String";
        	    Object object = hashtable.get(name);
        	    if(object != null) {
					valueClass = object.getClass().getName();
        	    }
        		String value = resultSet.getString(2);
        		if(valueClass.equals("java.lang.Boolean")) {
        			hashtable.put(name, new Boolean(value));
        		} else if(valueClass.equals("java.lang.Integer")) {
        			hashtable.put(name, new Integer(value));
        		} else if(valueClass.equals("java.lang.Double")) {
        			hashtable.put(name, new Double(value));
        		} else if(valueClass.equals("java.lang.Long")) {
        			hashtable.put(name, new Long(value));
        		} else if(valueClass.equals("java.lang.String")) {
        			hashtable.put(name, value);
        		}
           	}
        	statement.close();
        	connection.close();
		} catch (Exception e) {
	        e.printStackTrace();
	    }			
	}
	
	
	/**
	 * Sets proxy system properties: http.proxySet, http.proxyHost, 
	 * http.proxyPort to values stored in Settings.
	 */
	public static void refreshProxySystemProperties() {		
    	if(Settings.getBoolean("phrasesBankUseProxy")) {
        	System.getProperties().put("http.proxySet", true);
        	System.getProperties().put("http.proxyHost", 
        			Settings.getString("phrasesBankProxyServer"));
        	System.getProperties().put("http.proxyPort", 
        			Settings.getString("phrasesBankProxyPort"));
    	} else {
    		System.getProperties().remove("http.proxySet");
    		System.getProperties().remove("http.proxyHost");
    		System.getProperties().remove("http.proxyPort");
    	}   	
	}
}


